package com.quectel.mobile.controller.partybuilding;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.quectel.constant.core.partybuilding.PartyBuildingMeetingConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.partybuilding.dto.PartyPersonDto;
import com.quectel.core.module.partybuildingactivity.dto.PartyBuildingActivityDto;
import com.quectel.core.module.partybuildingactivity.dto.PartyBuildingActivityPersonJoinBindingDto;
import com.quectel.core.module.partybuildingactivity.service.PartyBuildingActivityPersonJoinBindingService;
import com.quectel.core.module.partybuildingactivity.service.PartyBuildingActivityService;
import com.quectel.mobile.controller.BaseController;
import com.quectel.mobile.util.SessionHolder;
import com.quectel.mobile.vo.partybuilding.PartyBuildingActivityReq;
import com.quectel.mobile.vo.partybuilding.PartyBuildingActivityResp;
import com.quectel.util.common.Page;
import com.quectel.util.common.Request;
import com.quectel.util.common.Response;
import com.quectel.util.ex.CheckErrorException;
import com.quectel.util.redis.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 在这里编写说明
 *
 * @author: bob
 * @email: bob.yu@quectel.com
 * @date: 2023-06-13 15:02:00
 */
@RestController
@RequestMapping("partyBuildingActivity")
@Tag(name = "PartyBuildingActivityController", description = "党建活动相关api")
public class PartyBuildingActivityController extends BaseController {

    @DubboReference
    private PartyBuildingActivityService partyBuildingActivityService;
    @DubboReference
    private PartyBuildingActivityPersonJoinBindingService activityPersonJoinBindingService;


    /**
     * 上报限制,根据activityId+personId
     */
    private static final String ACTIVITY_REPORT_LIMIT_LOCK_BY_PERSON = "ACTIVITY_REPORT_LIMIT_LOCK_BY_PERSON:";
    /**
     * 上报间隔
     */
    private static final int REPORT_TIME_LIMIT = 3;

    @PostMapping("sign")
    @Operation(summary = "签到")
    public Response<Object> sign(@RequestBody @Validated PartyBuildingActivityReq req) {
        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getPartyPerson();

        if (!RedisUtils.setnx(ACTIVITY_REPORT_LIMIT_LOCK_BY_PERSON + req.getId() + SystemConstants.COLON + partyPerson.getId(), SystemConstants.YES.toString(), REPORT_TIME_LIMIT, TimeUnit.SECONDS)) {
            return Response.error("操作过于频繁");
        }

        try {
            PartyBuildingActivityDto partyBuildingActivityDto = partyBuildingActivityService.selectById(req.getId());
            if (partyBuildingActivityDto == null) {
                return Response.error(SystemConstants.ILLEGAL_MSG);
            }

            PartyBuildingActivityPersonJoinBindingDto dbValue = activityPersonJoinBindingService.selectOne(req.getId(), partyPerson.getId());
            if (dbValue == null) {
                logger.error("未找到关联记录");
                return Response.error(SystemConstants.ILLEGAL_MSG);
            }

            if (dbValue.getSignStatus().equals(PartyBuildingMeetingConstants.SignStatus.SIGN.getStatus())) {
                return Response.ok().wrap("您已签到，无需重复签到");
            }

            if (!partyBuildingActivityDto.getStatus().equals(SystemConstants.YES) || !DateUtil.isIn(new Date(), partyBuildingActivityDto.getActivityStartTime(), partyBuildingActivityDto.getActivityEndTime())) {
                return Response.error("当前时间不在报名时间段");
            }

            // 需要报名的活动 看自己是否报名过 需要先报名后签到
            if (partyBuildingActivityDto.getAllowStatus().equals(SystemConstants.YES)
                    && dbValue.getJoinStatus().equals(PartyBuildingMeetingConstants.JoinStatus.JOIN.getStatus())) {

                // 签到动作
                PartyBuildingActivityPersonJoinBindingDto updately = new PartyBuildingActivityPersonJoinBindingDto();
                updately.setId(dbValue.getId());
                updately.setSignStatus(SystemConstants.YES);
                updately.setSignTime(new Date());
                activityPersonJoinBindingService.updateById(updately);
            }

            if (partyBuildingActivityDto.getAllowStatus().equals(SystemConstants.NO)) {
                // 签到动作
                PartyBuildingActivityPersonJoinBindingDto updately = new PartyBuildingActivityPersonJoinBindingDto();
                updately.setId(dbValue.getId());
                updately.setSignStatus(SystemConstants.YES);
                updately.setSignTime(new Date());
                activityPersonJoinBindingService.updateById(updately);
            }

        } catch (Exception e) {
            throw new CheckErrorException("签到异常", e);
        }
        return Response.ok();
    }

    @PostMapping("cancelJoin")
    @Operation(summary = "取消报名")
    public Response<Object> cancelJoin(@RequestBody @Validated PartyBuildingActivityReq req) {
        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getPartyPerson();

        Date currentDate = new Date();
        if (!RedisUtils.setnx(ACTIVITY_REPORT_LIMIT_LOCK_BY_PERSON + req.getId() + SystemConstants.COLON + partyPerson.getId(),
                SystemConstants.YES.toString(),
                REPORT_TIME_LIMIT, TimeUnit.SECONDS)) {
            return Response.error("操作过于频繁");
        }

        try {
            PartyBuildingActivityDto partyBuildingActivityDto = partyBuildingActivityService.selectById(req.getId());

            // 查看记录是否存在
            if (partyBuildingActivityDto == null) {
                return Response.error("活动不存在");
            }

            if (partyBuildingActivityDto.getAllowStatus().equals(SystemConstants.NO)) {
                return Response.error("该活动无需报名");
            }


            if (!DateUtil.isIn(currentDate, partyBuildingActivityDto.getJoinStartTime(), partyBuildingActivityDto.getJoinEndTime())) {
                return Response.error("当前时间不在报名时间段范围之内");
            }

            // 查看报名记录是否存在
            PartyBuildingActivityPersonJoinBindingDto activityPersonJoinBindingDto =
                    activityPersonJoinBindingService.selectOne(partyBuildingActivityDto.getId(), partyPerson.getId());
            if (activityPersonJoinBindingDto == null) {
                return Response.error(SystemConstants.ILLEGAL_MSG);
            }

            // 看是否已经签到，如果其签到了，不允许取消报名
            if (activityPersonJoinBindingDto.getSignStatus().equals(PartyBuildingMeetingConstants.SignStatus.SIGN.getStatus())){
                return Response.error("您已签到，不允许取消报名");
            }

            // 更新把报名时间 ->null
            activityPersonJoinBindingDto.setJoinStatus(SystemConstants.NO);
            activityPersonJoinBindingDto.setJoinTime(null);
            activityPersonJoinBindingService.updateAllColumnById(activityPersonJoinBindingDto);
        } catch (Exception e) {
            throw new CheckErrorException("取消报名异常");
        }
        return Response.ok();
    }

    @PostMapping("join")
    @Operation(summary = "报名")
    public Response<Object> join(@RequestBody @Validated PartyBuildingActivityReq req) {

        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getPartyPerson();

        if (!RedisUtils.setnx(ACTIVITY_REPORT_LIMIT_LOCK_BY_PERSON + req.getId() + SystemConstants.COLON + partyPerson.getId(),
                SystemConstants.YES.toString(),
                REPORT_TIME_LIMIT, TimeUnit.SECONDS)) {
            return Response.error("操作过于频繁");
        }

        try {
            /*
            校验记录状态
            status=1
            当前时间在报名开始和结束之间
            当前报名人数小于最大报名限制数
         */
            PartyBuildingActivityDto partyBuildingActivityDto = partyBuildingActivityService.selectById(req.getId());
            if (partyBuildingActivityDto != null) {
                // 无需报名
                if (partyBuildingActivityDto.getAllowStatus().equals(SystemConstants.NO)) {
                    return Response.error("该活动无需报名");
                }

                PartyBuildingActivityPersonJoinBindingDto dbValue = activityPersonJoinBindingService.selectOne(req.getId(), partyPerson.getId());

                if (dbValue == null) {
                    logger.error("未找到关联记录");
                    return Response.error(SystemConstants.ILLEGAL_MSG);
                }

                if (dbValue.getJoinStatus().equals(PartyBuildingMeetingConstants.JoinStatus.JOIN.getStatus())) {
                    return Response.error("您已报名,无需重复报名");
                }

                if (partyBuildingActivityDto.getStatus().equals(SystemConstants.YES)
                        &&
                        DateUtil.isIn(new Date(), partyBuildingActivityDto.getJoinStartTime(), partyBuildingActivityDto.getJoinEndTime())) {

                    // 校验是否超过最大报名数
                    Map<String, Object> objectMap = new HashMap<>();
                    objectMap.put(LambdaUtil.getFieldName(PartyBuildingActivityPersonJoinBindingDto::getPartyBuildingActivityId), req.getId());
                    objectMap.put(LambdaUtil.getFieldName(PartyBuildingActivityPersonJoinBindingDto::getJoinStatus), SystemConstants.YES);
                    int queryTotal = activityPersonJoinBindingService.queryTotal(objectMap);

                    // 当前报名人数小于限制数量
                    if (queryTotal < partyBuildingActivityDto.getLimitNum()) {
                        PartyBuildingActivityPersonJoinBindingDto updately = new PartyBuildingActivityPersonJoinBindingDto();
                        updately.setId(dbValue.getId());
                        updately.setJoinStatus(SystemConstants.YES);
                        updately.setJoinTime(new Date());
                        activityPersonJoinBindingService.updateById(updately);
                    } else {
                        return Response.error("报名人数已满");
                    }
                } else {
                    return Response.error("不在报名时间段");
                }
            } else {
                return Response.error(SystemConstants.ILLEGAL_MSG);
            }

        } catch (Exception e) {
            throw new CheckErrorException("报名异常");
        }
        return Response.ok();
    }

    @GetMapping("info/{id}")
    @Operation(summary = "根据id查询详情")
    public Response<PartyBuildingActivityDto> info(@PathVariable("id") Long id) {
        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getPartyPerson();

        PartyBuildingActivityDto activityDto = partyBuildingActivityService.selectById(id);
        if (activityDto == null) {
            return Response.error(SystemConstants.ILLEGAL_MSG);
        }
        PartyBuildingActivityPersonJoinBindingDto activityPersonJoinBindingDto =
                activityPersonJoinBindingService.selectOne(activityDto.getId(), partyPerson.getId());
        activityDto.setIsJoin(
                activityDto.getAllowStatus().equals(SystemConstants.YES)
                        &&
                        activityPersonJoinBindingDto.getJoinStatus()
                                .equals(PartyBuildingMeetingConstants.JoinStatus.JOIN.getStatus()) ?
                        SystemConstants.YES :
                        SystemConstants.NO
        );

        activityDto.setIsSign(
                activityPersonJoinBindingDto.getSignStatus()
                        .equals(PartyBuildingMeetingConstants.SignStatus.SIGN.getStatus()) ?
                        SystemConstants.YES :
                        SystemConstants.NO
        );

        return Response.<PartyBuildingActivityDto>ok().wrap(activityDto);
    }


    /**
     * 全部：显示全部状态；待参加：未签到的所有状态；已参加：已签到的所有状态；
     *
     * @param signStatus
     * @param pageNo
     * @param pageSize
     * @return
     */
    @GetMapping("list")
    @Operation(summary = "党建活动分页查询列表")
    public Response<Page<PartyBuildingActivityResp>> list(
            @Parameter(description = "参与状态 0待参与 1已参加") @RequestParam(required = false) Byte signStatus,
            @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
            @Parameter(description = "每页多少条") @RequestParam Integer pageSize
    ) {
        SessionHolder.checkPartyPerson();
        PartyPersonDto partyPerson = Objects.requireNonNull(SessionHolder.getLoginMobileUserSession()).getPartyPerson();
        Request request = Request.configParams(Request.parseParams(httpServletRequest));

        request.getParams().put(LambdaUtil.getFieldName(PartyBuildingActivityDto::getTenantId), partyPerson.getTenantId());
        request.getParams().put("joinIn", signStatus);
        request.getParams().put(LambdaUtil.getFieldName(PartyBuildingActivityPersonJoinBindingDto::getPartyPersonId), partyPerson.getId());

        int totalCount = partyBuildingActivityService.queryTotal(request.getParams());
        List<PartyBuildingActivityResp> respList = new ArrayList<>();
        List<PartyBuildingActivityDto> list = null;
        if (totalCount > 0) {
            list = partyBuildingActivityService.queryListWithoutPadding(request.getParams());

            for (PartyBuildingActivityDto activityDto : list) {
                PartyBuildingActivityResp resp = new PartyBuildingActivityResp();
                resp.setId(activityDto.getId());
                resp.setSubject(activityDto.getSubject());
                resp.setAddress(activityDto.getAddress());
                resp.setCoverImg(activityDto.getCoverImg());
                resp.setAllowStatus(activityDto.getAllowStatus());
                resp.setJoinStartTime(activityDto.getJoinStartTime());
                resp.setJoinEndTime(activityDto.getJoinEndTime());
                resp.setActivityStartTime(activityDto.getActivityStartTime());
                resp.setActivityEndTime(activityDto.getActivityEndTime());
                resp.setStatus(activityDto.getStatus());
                resp.setCreateTime(activityDto.getCreateTime());

                Map<String, Object> sqlMap = new HashMap<>();
                sqlMap.put(LambdaUtil.getFieldName(PartyBuildingActivityPersonJoinBindingDto::getPartyBuildingActivityId), activityDto.getId());
                int totalJoinCount = activityPersonJoinBindingService.queryTotal(sqlMap);
                resp.setTotalJoinCount(totalJoinCount);

                // 签到
                sqlMap.put(LambdaUtil.getFieldName(PartyBuildingActivityPersonJoinBindingDto::getJoinStatus), PartyBuildingMeetingConstants.JoinStatus.JOIN.getStatus());
                int joinCount = activityPersonJoinBindingService.queryTotal(sqlMap);
                sqlMap.remove(LambdaUtil.getFieldName(PartyBuildingActivityPersonJoinBindingDto::getJoinStatus));
                resp.setJoinCount(joinCount);

                PartyBuildingActivityPersonJoinBindingDto activityPersonJoinBindingDto =
                        activityPersonJoinBindingService.selectOne(activityDto.getId(), partyPerson.getId());

                resp.setIsJoin(
                        activityDto.getAllowStatus().equals(SystemConstants.YES)
                                &&
                                activityPersonJoinBindingDto.getJoinStatus()
                                        .equals(PartyBuildingMeetingConstants.JoinStatus.JOIN.getStatus()) ?
                                SystemConstants.YES :
                                SystemConstants.NO
                );

                resp.setIsSign(
                        activityPersonJoinBindingDto.getSignStatus()
                                .equals(PartyBuildingMeetingConstants.SignStatus.SIGN.getStatus()) ?
                                SystemConstants.YES :
                                SystemConstants.NO
                );

                respList.add(resp);
            }
        } else {
            respList = new ArrayList<>();
        }
        Page<PartyBuildingActivityResp> page = new Page<>(request.getPageNo(), request.getPageSize(), totalCount, respList);
        return Response.<Page<PartyBuildingActivityResp>>ok().wrap(page);
    }

}
